package cn.keniykey.mc.viewlibrary.container.api;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.Nullable;

import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import com.google.common.collect.ImmutableList;

import cn.keniykey.mc.viewlibrary.ViewLibraryMain;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowCloseDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowItemsDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowOpenDispatch;
import cn.keniykey.mc.viewlibrary.packet.api.PacketWindowSetSlotDispatch;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionClass;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionConstructor;

public class Container {
	private static final ConcurrentHashMap<UUID, Container> hashmap_players_containerview = new ConcurrentHashMap<>();
	
	@Nullable
	public static Container getViewingContainer(Player player) {
		final UUID playeruuid = player.getUniqueId();
		if(hashmap_players_containerview.containsKey(playeruuid)) return hashmap_players_containerview.get(playeruuid);
		return null;
	}
	public static void setViewingContainer(Player player,@Nullable Container container) {
		if(container == null) {
			hashmap_players_containerview.remove(player.getUniqueId());
			return;
		}
		hashmap_players_containerview.put(player.getUniqueId(), container);
	}
	public static boolean hasViewingContainer(Player player) {
		return hashmap_players_containerview.containsKey(player.getUniqueId());
	}
	static final HashMap<EContainerType,ReflectionConstructor> hashmap_containers_type;
	
	public static void putContainerType(EContainerType type,ReflectionConstructor constructor) {
		hashmap_containers_type.put(type, constructor);
	}
	
	static {
		hashmap_containers_type = new HashMap<>();
		hashmap_containers_type.put(EContainerType.ANVIL, new ReflectionClass(ContainerAnvil.class).getConstructor());
	}
	
	private EContainerType type;
	private String title = "Container";
	private ItemStack[] items;
	private @Nullable IContainerOpenHandler handler_open;
	private @Nullable IContainerClickHandler handler_click;
	private @Nullable IContainerCloseHandler handler_close;
	
	private final HashSet<UUID> viewers = new HashSet<>();
	
	protected Container(EContainerType con_type) {
		this.type = con_type;
		this.items = new ItemStack[con_type.getContainerMaxCapacity()];
	}
	public EContainerType getEContainergetType() {
		return this.type;
	}
	/**
	 * 改变Title与Container类型均重新打开界面视图
	 * 因此本操作包含，刷新标题，刷新界面类型，刷新物品显示
	 */
	public void setEContainergetType(EContainerType type1) {
		this.type = type1;
		if(this.type.getContainerMaxCapacity() == this.items.length) return;
		final ItemStack[] array_items = new ItemStack[this.type.getContainerMaxCapacity()];
		for(int index = 0; index < array_items.length && index < this.items.length; index++) array_items[index] = this.items[index];
		this.clearItemStacks();
		this.items = array_items;
		this.refresh();
	}
	@Nullable
	public IContainerOpenHandler getIContainerOpenHandler() {
		return this.handler_open;
	}
	public void setIContainerOpenHandler(@Nullable IContainerOpenHandler handler_open) {
		this.handler_open = handler_open;
	}
	@Nullable
	public IContainerClickHandler getIContainerClickHandler() {
		return this.handler_click;
	}
	public void setIContainerClickHandler(@Nullable IContainerClickHandler handler_click) {
		this.handler_click = handler_click;
	}
	@Nullable
	public IContainerCloseHandler getIContainerCloseHandler() {
		return handler_close;
	}
	public void setIContainerCloseHandler(@Nullable IContainerCloseHandler handler_close) {
		this.handler_close = handler_close;
	}
	public List<Player> getViewers() {
		final ImmutableList.Builder<Player> list = ImmutableList.builder();
		for(final UUID playeruuid : this.viewers) list.add(Bukkit.getPlayer(playeruuid));
		return list.build();
	}
	public int getContainerSize() {
		return this.items.length;
	}
	public String getTitle() {
		return this.title;
	}
	/**
	 * 改变Title与Container类型均重新打开界面视图
	 * 因此本操作包含，刷新标题，刷新界面类型，刷新物品显示
	 */
	public void setTitle(String title1) {
		if(null == title1 || title1.isEmpty()) throw new RuntimeException("Container title can not be null or empty(容器的标题不可为NULL或空白)");
		this.refresh();
	}
	public void open(Player player) {
		final Container containe = getViewingContainer(player);
		if(null != containe) containe.viewers.remove(player.getUniqueId());
		setViewingContainer(player, this);
		final UUID player_uuid = player.getUniqueId();
		this.viewers.add(player_uuid);
		if(null == this.handler_open) return;
		try {
			this.handler_open.onContainerOpenResponse(player, this);
		}catch(Exception ex) {
			ex.printStackTrace();
		}
		if(this.viewers.contains(player_uuid)) this.refresh(player);
	}
	public void refresh() {
		if(this.viewers.isEmpty()) return;
		Bukkit.getScheduler().runTaskAsynchronously(ViewLibraryMain.getPlugin(), ()->{
			final PacketWindowOpenDispatch packet_open = new PacketWindowOpenDispatch(this.type, this.title);
			final PacketWindowItemsDispatch packet_items = new PacketWindowItemsDispatch(Arrays.asList(this.items));
			this.viewers.forEach((UUID playeruuid) ->{
				final Player player = Bukkit.getPlayer(playeruuid);
				if(null != player) {
					packet_open.sendPacket(player, true);
					packet_items.sendPacket(player, true);
				}
			});
		});
	}
	public void refresh(Player player) {
		if(!this.viewers.contains(player.getUniqueId())) return;
		Bukkit.getScheduler().runTaskAsynchronously(ViewLibraryMain.getPlugin(), ()->{
			new PacketWindowOpenDispatch(this.type, this.title).sendPacket(player);
			new PacketWindowItemsDispatch(Arrays.asList(this.items)).sendPacket(player);
		});
	}
	/**
	 * 关闭后不执行清空。若确认不再使用，请调用clearItemStack
	 * @param close_packet 若第三方调用除非特殊封装，否则必须true
	 * @param initiative 是否是主动调用（被动则为着不可抗拒式的关闭调用）
	 */
	@SuppressWarnings("deprecation")
	public void close(Player player,boolean close_packet,boolean initiative) {
		if(close_packet) {
			Bukkit.getScheduler().runTaskAsynchronously(ViewLibraryMain.getPlugin(), ()->{
				new PacketWindowCloseDispatch().sendPacket(player);
			});
			Bukkit.getScheduler().runTaskLater(ViewLibraryMain.getPlugin(), ()->{
				player.updateInventory();
			}, 5l);
		}
		this.viewers.remove(player.getUniqueId());
		setViewingContainer(player, null);
		if(null == this.handler_close) return;
		try {
			this.handler_close.onContainerCloseResponse(player, this, initiative);
		}catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	/**
	 * 关闭后不执行清空。若确认不再使用，请调用clearItemStack
	 */
	public void close(Player player) {
		this.close(player, true, true);
	}
	public void refreshItemStack() {
		Bukkit.getScheduler().runTaskAsynchronously(ViewLibraryMain.getPlugin(), ()->{
			final PacketWindowItemsDispatch packet = new PacketWindowItemsDispatch(Arrays.asList(this.items));
			this.viewers.forEach((UUID playeruuid) -> {
				final Player player = Bukkit.getPlayer(playeruuid);
				if(null != player) packet.sendPacket(player, true);
			});
		});
	}
	public void refreshItemStack(Player player) {
		if(!this.viewers.contains(player.getUniqueId())) return;
		Bukkit.getScheduler().runTaskAsynchronously(ViewLibraryMain.getPlugin(), () -> {
			new PacketWindowItemsDispatch(Arrays.asList(this.items)).sendPacket(player);
		});
	}
	public void refreshItemStack(int slot) {
		if(slot < 0 || slot >= this.items.length) {
			throw new ArrayIndexOutOfBoundsException("Index: " + slot + " MaxIndex: " + this.items.length);
		}
		if(this.viewers.isEmpty()) return;
		Bukkit.getScheduler().runTaskAsynchronously(ViewLibraryMain.getPlugin(), () -> {
			final PacketWindowSetSlotDispatch packet = new PacketWindowSetSlotDispatch(slot, this.items[slot]);
			this.viewers.forEach( (UUID playeruuid) -> {
				final Player player = Bukkit.getPlayer(playeruuid);
				if(null != player) packet.sendPacket(player, true);
			});
		});
	}
	@Nullable
	public ItemStack getItemStack(int slot) {
		if(slot < 0 || slot >= this.items.length) {
			throw new ArrayIndexOutOfBoundsException("Index: " + slot + " MaxIndex: " + this.items.length);
		}
		return this.items[slot];
	}
	public void setItemStack(int slot,@Nullable ItemStack item,boolean display_packet) {
		if(slot < 0 || slot >= this.items.length) {
			throw new ArrayIndexOutOfBoundsException("Index: " + slot + " MaxIndex: " + this.items.length);
		}
		this.items[slot] = (null == item ? null : item.clone());
		if(!display_packet) return;
		this.refreshItemStack(slot);
	}
	public void setItemStack(int slot,@Nullable ItemStack item) {
		this.setItemStack(slot, item, true);
	}
	public void clearItemStacks() {
		for(int index = 0; index < this.items.length; index++) this.items[index] = null;
	}
	
	public static Container createContainer(EContainerType type) {
		if(!Container.hashmap_containers_type.containsKey(type)) return new Container(type);
		final ReflectionConstructor constructor = Container.hashmap_containers_type.get(type);
		return (Container) constructor.newInstance();
	}
}